Creating Custom Agents
While the base Agent
class provides a solid foundation for building AI agents, you may want to create custom agent types with specialized behaviors. This guide explains how to extend the base Agent
class to create your own custom agents.
Extending the Agent Class
You can create custom agents by extending the base Agent
class:
from SimplerLLM.agents import Agent
from SimplerLLM.language import LLM
class CustomAgent(Agent):
"""
A custom agent with specialized behavior.
"""
def __init__(self, llm: LLM, **kwargs):
# Custom initialization
super().__init__(llm, **kwargs)
# Add custom properties
self.custom_property = "custom value"
def run(self, user_input: str, max_iterations: int = 10) -> str:
"""
Override the run method to implement custom behavior.
"""
# Custom pre-processing
processed_input = self._preprocess_input(user_input)
# Call the parent run method
response = super().run(processed_input, max_iterations)
# Custom post-processing
final_response = self._postprocess_response(response)
return final_response
def _preprocess_input(self, user_input: str) -> str:
"""
Custom method to preprocess user input.
"""
# Example: Add context to the user input
return f"Context: The user is asking about X. User input: {user_input}"
def _postprocess_response(self, response: str) -> str:
"""
Custom method to postprocess the agent's response.
"""
# Example: Add a disclaimer to the response
return f"{response}\n\nNote: This response was generated by a custom agent."
Example: Creating a Research Agent
Here's an example of a specialized research agent that automatically searches for information:
from SimplerLLM.agents import Agent
from SimplerLLM.language import LLM
from SimplerLLM.tools.serp import search_with_duck_duck_go
from typing import List, Dict, Any
class ResearchAgent(Agent):
"""
A specialized agent for research tasks that automatically searches for information.
"""
def __init__(self, llm: LLM, **kwargs):
super().__init__(llm, **kwargs)
# Add search tool by default
self.add_tool(
name="web_search",
func=search_with_duck_duck_go,
description="Search the web for information",
parameters={
"query": "The search query string",
"max_results": "Maximum number of results to return (default: 5)"
}
)
# Set a research-focused system prompt
research_prompt = """You are a research assistant specialized in finding and synthesizing information.
For each query:
1. Identify the key research questions
2. Automatically search for relevant information
3. Synthesize the information into a clear, comprehensive response
4. Cite your sources
Always search for information before attempting to answer from your own knowledge."""
# Update the system prompt
self.memory.add_system_message(research_prompt)
def run(self, user_input: str, max_iterations: int = 15) -> str:
"""
Override run to automatically perform searches for research queries.
"""
# Add user input to memory
self.memory.add_user_message(user_input)
# Automatically generate search queries based on the user input
search_queries = self._generate_search_queries(user_input)
# Perform searches and add results to memory
for query in search_queries:
if self.verbose:
print(f"Automatically searching for: {query}")
try:
search_results = search_with_duck_duck_go(query=query, max_results=3)
result_text = "\n\n".join([
f"Title: {result.Title}\nURL: {result.URL}\nDescription: {result.Description}"
for result in search_results
])
self.memory.add_assistant_message(
f"Search results for '{query}':\n{result_text}"
)
except Exception as e:
if self.verbose:
print(f"Error searching for '{query}': {str(e)}")
# Now run the standard agent loop with the search results in memory
return super().run(
"Based on the search results above, please provide a comprehensive answer to my question.",
max_iterations
)
def _generate_search_queries(self, user_input: str) -> List[str]:
"""
Generate search queries based on the user input.
In a real implementation, this could use the LLM to generate queries.
For simplicity, this example just uses the user input as a query.
"""
# Simple implementation - just use the user input as a query
return [user_input]
Example: Creating a Coding Agent
Here's an example of a specialized coding agent that helps with programming tasks:
from SimplerLLM.agents import Agent
from SimplerLLM.language import LLM
from SimplerLLM.tools.python_func import execute_python_code
from SimplerLLM.tools.file_functions import save_text_to_file
class CodingAgent(Agent):
"""
A specialized agent for coding tasks.
"""
def __init__(self, llm: LLM, **kwargs):
super().__init__(llm, **kwargs)
# Add coding-related tools
self.add_tool(
name="execute_python",
func=execute_python_code,
description="Execute Python code and return the result",
parameters={
"input_code": "Python code to execute"
}
)
self.add_tool(
name="save_code",
func=save_text_to_file,
description="Save code to a file",
parameters={
"text": "Code content to save",
"filename": "Name of the file to save to"
}
)
# Set a coding-focused system prompt
coding_prompt = """You are a coding assistant specialized in writing and debugging code.
For each coding task:
1. Think through the problem step by step
2. Write clean, well-documented code
3. Test your code by executing it
4. Debug any issues that arise
5. Explain your solution clearly
Always test your code before providing a final solution."""
# Update the system prompt
self.memory.add_system_message(coding_prompt)
Example: Creating a Multi-Agent System
You can also create more complex systems with multiple agents working together:
from SimplerLLM.agents import Agent
from SimplerLLM.language import LLM
from typing import Dict, List, Any
class MultiAgentSystem:
"""
A system that coordinates multiple specialized agents.
"""
def __init__(self, llm: LLM):
self.llm = llm
self.agents: Dict[str, Agent] = {}
def add_agent(self, name: str, agent: Agent):
"""
Add an agent to the system.
"""
self.agents[name] = agent
def run(self, user_input: str) -> str:
"""
Process the user input by routing it to the appropriate agent.
"""
# Determine which agent should handle the request
agent_name = self._route_request(user_input)
if agent_name in self.agents:
# Run the selected agent
return self.agents[agent_name].run(user_input)
else:
return f"No agent found to handle: {user_input}"
def _route_request(self, user_input: str) -> str:
"""
Determine which agent should handle the request.
In a real implementation, this could use the LLM to classify the request.
For simplicity, this example uses a basic keyword matching approach.
"""
# Simple keyword-based routing
if "code" in user_input.lower() or "programming" in user_input.lower():
return "coding"
elif "research" in user_input.lower() or "information" in user_input.lower():
return "research"
else:
return "general" # Default agent
Best Practices for Custom Agents
When creating custom agents, consider these best practices:
-
Extend, Don't Replace: Build on the existing functionality of the base
Agent
class rather than replacing it entirely. -
Specialized System Prompts: Craft system prompts that guide the agent toward its specialized behavior.
-
Default Tools: Add default tools that are relevant to the agent's specialty.
-
Custom Pre/Post Processing: Add methods for preprocessing user input and postprocessing agent responses.
-
Error Handling: Implement robust error handling, especially for tool execution.
-
Documentation: Document your custom agent's behavior and any special requirements.
-
Testing: Create unit tests for your custom agent to ensure it behaves as expected.
Advanced Customization
For more advanced customization, you can override these methods:
_create_prompt()
: Customize how the prompt is generated_get_system_prompt()
: Customize how the system prompt is retrievedrun()
: Completely customize the agent's execution looprun_async()
: Implement a custom asynchronous execution loop
Conclusion
Creating custom agents allows you to build specialized AI assistants tailored to specific tasks or domains. By extending the base Agent
class, you can leverage the existing functionality while adding your own specialized behaviors.